package pl.edu.pw.elka.tinyrepo.client.controller;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;

import pl.edu.pw.elka.tinyrepo.client.Client;
import pl.edu.pw.elka.tinyrepo.client.DownloadHandler;
import pl.edu.pw.elka.tinyrepo.client.common.COPYRequest;
import pl.edu.pw.elka.tinyrepo.client.common.DELETERequest;
import pl.edu.pw.elka.tinyrepo.client.common.FakeResponse;
import pl.edu.pw.elka.tinyrepo.client.common.GETRequest;
import pl.edu.pw.elka.tinyrepo.client.common.MKCOLRequest;
import pl.edu.pw.elka.tinyrepo.client.common.MOVERequest;
import pl.edu.pw.elka.tinyrepo.client.common.MockRequest;
import pl.edu.pw.elka.tinyrepo.client.common.PROPFINDRequest;
import pl.edu.pw.elka.tinyrepo.client.common.PUTRequest;
import pl.edu.pw.elka.tinyrepo.client.common.RepoTreeModel;
import pl.edu.pw.elka.tinyrepo.client.view.GateAddressDialog;
import pl.edu.pw.elka.tinyrepo.client.view.View;
import pl.edu.pw.elka.tinyrepo.common.CatalogContent;
import pl.edu.pw.elka.tinyrepo.common.CatalogContentSerializer;
import pl.edu.pw.elka.tinyrepo.common.FileProperties;
import pl.edu.pw.elka.tinyrepo.common.FilePropertiesSerializer;
import pl.edu.pw.elka.tinyrepo.common.HTTPHeaderType;
import pl.edu.pw.elka.tinyrepo.common.StatusCode;


/**
 * Kotroler aplikacji klienta
 * 
 * @author Anna Stępień
 * @version 12-12-2011
 *
 */
public class Controller
{
	/** Obiekt klienta (model) */
	private Client client;
	/** Widok klienta */
	private final View view;
	/** Kolejka blokująca Requestów */
	private final BlockingQueue<MockRequest> requestQueue;
	/** Mapa przechowująca handlery do odpowiednich Requestów */
	private final Map<Class<? extends MockRequest>, ClientRequestHandler> handlersMap = 
			new HashMap<Class<? extends MockRequest>, ClientRequestHandler>();
	
	
	/**
	 * Tworzy nowy kontroler
	 * 
	 * @param client klient 
	 * @param requestQueue kolejka blokująca
	 * 
	 */
	public Controller(final View view, final BlockingQueue<MockRequest> requestQueue)
	{
		this.view = view;
		this.requestQueue = requestQueue;
		registerRequest(GETRequest.class, new GETRequestHandler());
		registerRequest(PUTRequest.class, new PUTRequestHandler());
		registerRequest(MOVERequest.class, new MOVERequestHandler());
		registerRequest(COPYRequest.class, new COPYRequestHandler());
		registerRequest(DELETERequest.class, new DELETERequestHandler());
		registerRequest(MKCOLRequest.class, new MKCOLRequestHandler());
		registerRequest(PROPFINDRequest.class, new PROPFINDRequestHandler());
	}
	
	/**
	 * Uruchamia kontroler
	 * 
	 */
	public void start()
	{
		//przed rozpoczęciem pracy pobierz adres i port bramy
		//i na podstawie tych danych utwórz odpowiedni obiekt klienta
		initClient();
		
		while (true)
		{
			final MockRequest request;
			try
			{
				request = requestQueue.take();
				handlersMap.get(request.getClass()).handleRequest(request);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
	
	private class GETRequestHandler extends ClientRequestHandler
	{

		@Override
		public void handleRequest(MockRequest request)
		{
			GETRequest getRequest = (GETRequest) request;
			FakeResponse response = client.get(getRequest.getPath());
			//TODO: Obłsuga zapisu pliku albo wyświetlenie kolekcji
			
			//mamy xml lub plik - jeśli wszystko jest ok
			if (response.getStatusCode() == StatusCode.OK)
			{
				//mamy plik xml z danymi o katalogu
				if (response.getHeader(HTTPHeaderType.CONTENT_TYPE) != null && 
						response.getHeader(HTTPHeaderType.CONTENT_TYPE).equals("text/xml"))
				{
					//przekaż dane do widoku
					//i wyświetl drzewo katalogów
					CatalogContentSerializer serializer = new CatalogContentSerializer();
					String xmlResponse = streamToString(response.getDataStream());
					CatalogContent content = serializer.fromXML(xmlResponse);
					
					RepoTreeModel treeModel = new RepoTreeModel(getRequest.getPath(), 
							content.getSubdirectoriesList(), content.getFilesList());
					
					view.displayTree(treeModel);
				}
				//mamy plik 
				else
				{
					//przekaż sterowanie do odpowiedniego wątku
					Thread downloadThread = new Thread(new DownloadHandler(response.getDataStream(), 
							getRequest.getDestinationPath()));
					downloadThread.start();
				}
			}
			//nie ma tego czego szukamy
			//wyświetl okienko
			else
			{
				view.displayMessage(response.getStatusCode().toString());
			}
		}
		
	}
	
	private class PUTRequestHandler extends ClientRequestHandler
	{

		@Override
		public void handleRequest(MockRequest request)
		{
			PUTRequest putRequest = (PUTRequest) request;
			// Otwieranie strumienia do pliku
			FileInputStream istr = null;
			File f = new File(putRequest.getFilePath());
			if(!f.isFile()) {
			   System.err.println("This is not file: "+f.getAbsolutePath()); // FIXME - wyswietlac komunikat w oknie
			}
			Long size = f.length();
         try {
            istr = new FileInputStream(f) ;
         } catch (FileNotFoundException e) {
            e.printStackTrace();
            return;
         }
         
         // przekazanie do klienta miejsca docelowego i strumienia pliku 
			FakeResponse response = client.put(putRequest.getDestinationPath(), size, istr);
			//wyświetl okienko ze statusem
			view.displayMessage(response.getStatusCode().toString());
		}
		
	}
	
	private class MOVERequestHandler extends ClientRequestHandler
	{

		@Override
		public void handleRequest(MockRequest request)
		{
			System.out.println("MOVE request");
			MOVERequest moveRequest = (MOVERequest) request;
			FakeResponse response = client.move(moveRequest.getSourcePath(), moveRequest.getDestinationPath());
			//wyświetl okienko ze statusem
			view.displayMessage(response.getStatusCode().toString());
		}
		
	}
	
	private class COPYRequestHandler extends ClientRequestHandler
	{

		@Override
		public void handleRequest(MockRequest request)
		{
			COPYRequest copyRequest = (COPYRequest) request;
			FakeResponse response = client.copy(copyRequest.getSourcePath(), copyRequest.getDestinationPath());
			//wyświetl okienko ze statusem
			view.displayMessage(response.getStatusCode().toString());
		}
		
	}
	
	private class DELETERequestHandler extends ClientRequestHandler
	{

		@Override
		public void handleRequest(MockRequest request)
		{
			DELETERequest deleteRequest = (DELETERequest) request;
			FakeResponse response = client.delete(deleteRequest.getPath());
			//wyświetl okienko ze statusem
			view.displayMessage(response.getStatusCode().toString());
		}
		
	}
	
	private class PROPFINDRequestHandler extends ClientRequestHandler
	{

		@Override
		public void handleRequest(MockRequest request)
		{
			PROPFINDRequest propfindRequest = (PROPFINDRequest) request;
			FakeResponse response = client.propfind(propfindRequest.getPath());
			
			if (response.getStatusCode() == StatusCode.OK)
			{
				//odczytaj dane i wyświetl
				FilePropertiesSerializer serializer = new FilePropertiesSerializer();
				String xmlResponse = streamToString(response.getDataStream());
				FileProperties properties = serializer.fromXML(xmlResponse);
				System.out.println(properties.getFilename());
				view.displayFileProperties(properties);
			}
			else
			{
				view.displayMessage(response.getStatusCode().toString());
			}
		}
		
	}
	
	private class MKCOLRequestHandler extends ClientRequestHandler
	{

		@Override
		public void handleRequest(MockRequest request)
		{
			System.out.println("MKCOL request");
			MKCOLRequest mkcolRequest = (MKCOLRequest) request;
			FakeResponse response = client.mkcol(mkcolRequest.getPath());
			
			//wyświetl okienko ze statusem
			view.displayMessage(response.getStatusCode().toString());
		}
		
	}

	private void registerRequest(final Class<? extends MockRequest> request, final ClientRequestHandler handler)
	{
		handlersMap.put(request, handler);
	}
	
	private void initClient()
	{
		GateAddressDialog dialog = new GateAddressDialog();
		dialog.getInput();
		client = new Client(dialog.getGateAddress(), dialog.getGatePort());
	}
	
	
	private String streamToString(final InputStream stream)
	{
		String result = "";
		InputStreamReader inputReader = new InputStreamReader(stream);
		BufferedReader bufferedReader = new BufferedReader(inputReader);
		String line;
		try 
		{
			line = bufferedReader.readLine();
			while(line != null)
			{
				result += line;
				line = bufferedReader.readLine();
			}
		}
		catch (Exception e) 
		{
			e.printStackTrace();
		}
		
		return result;
	}
}
